package com.shanshan.wheel.rpc.common.remoting.transport.server;

import com.shanshan.wheel.rpc.ApplicationContextUtil;
import com.shanshan.wheel.rpc.common.compress.CompressType;
import com.shanshan.wheel.rpc.common.remoting.constant.RpcMessageType;
import com.shanshan.wheel.rpc.common.remoting.constant.RpcResponseCode;
import com.shanshan.wheel.rpc.common.remoting.dto.RpcMessage;
import com.shanshan.wheel.rpc.common.remoting.dto.RpcRequest;
import com.shanshan.wheel.rpc.common.remoting.dto.RpcResponse;
import com.shanshan.wheel.rpc.common.remoting.transport.handler.RpcRequestHandler;
import com.shanshan.wheel.rpc.common.serialize.SerializationType;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;

/**
 * @Description
 * @ClassName NettyRpcServerHandler
 * @Author 山山大魔王
 * @Date 2022/9/4 23:19
 * @Version 1.0
 */
@Slf4j
public class NettyRpcServerHandler extends SimpleChannelInboundHandler<RpcMessage> {

    private RpcRequestHandler rpcRequestHandler;

    public NettyRpcServerHandler(){
        ApplicationContext applicationContext = ApplicationContextUtil.getApplicationContext();

        rpcRequestHandler = applicationContext.getBean(RpcRequestHandler.class);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, RpcMessage msg) throws Exception {
        log.info("服务器接收消息：[{}]", msg);
        // 获取消息类型
        byte messageType = msg.getMessageType();
        // 创建返回的消息对象
        RpcMessage returnMsg = new RpcMessage();
        // 设置压缩类型
        returnMsg.setCompress(CompressType.GZIP.getCode());
        // 设置序列化类型
        returnMsg.setCodec(SerializationType.KRYO.getCode());
        // 判断消息类型
        if(messageType == RpcMessageType.HEARTBEAT_PING_TYPE.getCode()){
            // 处理心跳ping消息
            // 设置消息类型为心跳pong
            returnMsg.setMessageType(RpcMessageType.HEARTBEAT_PONG_TYPE.getCode());
            // 设置数据
            returnMsg.setData("PONG");
        }else {
            // 处理请求消息
            RpcRequest rpcRequest = (RpcRequest) msg.getData();
            // 调用请求处理程序，获取结果
            Object result = rpcRequestHandler.handle(rpcRequest);
            log.info("服务获取的结果：[{}]",result.toString());
            // 设置类型为响应类型
            returnMsg.setMessageType(RpcMessageType.RESPONSE_TYPE.getCode());
            // 判断通道是否连接正常且立即可用
            if(ctx.channel().isActive() && ctx.channel().isWritable()){
                // 创建响应成功对象并设置好请求id
                RpcResponse<Object> rpcResponse = RpcResponse.success(result, rpcRequest.getRequestId());
                // 将响应对象设置到返回消息对象的数据字段中
                returnMsg.setData(rpcResponse);
            }else {
                // 创建响应失败对象
                RpcResponse<Object> rpcResponse = RpcResponse.fail(RpcResponseCode.FAIL);
                // 将响应对象设置到返回消息对象的数据字段中
                returnMsg.setData(rpcResponse);
                log.error("连接不可用，消息发送失败");
            }
            ctx.writeAndFlush(returnMsg).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
        }

    }

    /**
     * 监听用户事件
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof IdleStateEvent){ // 判断是否为 IdleStateEvent 事件
            IdleState state = ((IdleStateEvent) evt).state();
            // 判断 触发 IdleStateEvent事件触发原因是否是有一段时间没有收到数据
            if(state == IdleState.READER_IDLE){  // 是，关闭连接
                log.info("30s为接收数据，关闭连接.....");
                ctx.close();
            }else { // 不是，转发到ChannelPipeline的下一个ChannelInboundHandler
                super.userEventTriggered(ctx, evt);
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("服务捕获异常");
        cause.printStackTrace();
        ctx.close();
    }
}
